package simpledb.buffer;

import simpledb.server.SimpleDB;
import simpledb.file.*;

/**
 * An individual buffer. A buffer wraps a page and stores information about its
 * status, such as the disk block associated with the page, the number of times
 * the block has been pinned, whether the contents of the page have been
 * modified, and if so, the id of the modifying transaction and the LSN of the
 * corresponding log record.<br>
 * 单个缓冲区。 缓冲区包裹页面并存储关于其状态的信息，例如与页面相关联的磁盘块，块已被固定的次数，页面的内容是否已被修改，并且如果是，
 * 修改事务和相应日志记录的LSN。
 * 
 * @author Edward Sciore
 */
public class Buffer {
  private Page contents = new Page();
  private Block blk = null;
  private int pins = 0; // 通过判断pins的值，来标记Buffer是否被使用
  private int modifiedBy = -1; // negative means not modified负值表示不修改
  // negative means no corresponding log record负表示没有相应的日志记录
  private int logSequenceNumber = -1;

  /**
   * Creates a new buffer, wrapping a new {@link simpledb.file.Page page}. This
   * constructor is called exclusively by the class {@link BasicBufferMgr}. It
   * depends on the {@link simpledb.log.LogMgr LogMgr} object that it gets from
   * the class {@link simpledb.server.SimpleDB}. That object is created during
   * system initialization. Thus this constructor cannot be called until
   * {@link simpledb.server.SimpleDB#initFileAndLogMgr(String)} or is called
   * first.<br>
   * 创建一个新的缓冲区，包装一个新的{@link simpledb.file.Page页面}。 这个 构造函数仅由类
   * {@link BasicBufferMgr}调用。 它 取决于它从中获得的{@link simpledb.log.LogMgr LogMgr}对象 类
   * {@link simpledb.server.SimpleDB}。 该对象在创建期间 系统初始化。 因此，这个构造函数不能被调用或被
   * {@link simpledb.server.SimpleDB＃initFileAndLogMgr（String)}首先调用
   */
  public Buffer() {
  }

  /**
   * Returns the integer value at the specified offset of the buffer's page. If
   * an integer was not stored at that location, the behavior of the method is
   * unpredictable.<br>
   * 返回缓冲区页面指定偏移处的整数值。 如果整数未存储在该位置，则该方法的行为是不可预测的。
   * 
   * @param offset the byte offset of the page
   * @return the integer value at that offset
   */
  public int getInt(int offset) {
    return contents.getInt(offset);
  }

  /**
   * Returns the string value at the specified offset of the buffer's page. If a
   * string was not stored at that location, the behavior of the method is
   * unpredictable.<br>
   * 返回缓冲区页面指定偏移处的字符串值。 如果字符串未存储在该位置，则该方法的行为是不可预测的。
   * 
   * @param offset the byte offset of the page
   * @return the string value at that offset
   */
  public String getString(int offset) {
    return contents.getString(offset);
  }

  /**
   * Writes an integer to the specified offset of the buffer's page. This method
   * assumes that the transaction has already written an appropriate log record.
   * The buffer saves the id of the transaction and the LSN of the log record. A
   * negative lsn value indicates that a log record was not necessary.<br>
   * 将整数写入缓冲区页面的指定偏移量。 此方法假定事务已写入适当的日志记录。 缓冲区保存事务的id和日志记录的LSN。 负lsn值表示不需要日志记录。
   * 
   * @param offset the byte offset within the page 页面中的字节偏移
   * @param val the new integer value to be written 要写入的新整数值
   * @param txnum the id of the transaction performing the modification
   *          执行修改的事务的id
   * @param lsn the LSN of the corresponding log record 对应日志记录的LSN
   */
  public void setInt(int offset, int val, int txnum, int lsn) {
    modifiedBy = txnum;
    if (lsn >= 0)
      logSequenceNumber = lsn;
    contents.setInt(offset, val);
  }

  /**
   * Writes a string to the specified offset of the buffer's page. This method
   * assumes that the transaction has already written an appropriate log record.
   * A negative lsn value indicates that a log record was not necessary. The
   * buffer saves the id of the transaction and the LSN of the log record.<br>
   * 将字符串写入缓冲区页面的指定偏移量。 此方法假定事务已写入适当的日志记录。 负lsn值表示不需要日志记录。 缓冲区保存事务的id和日志记录的LSN。
   * 
   * @param offset the byte offset within the page
   * @param val the new string value to be written
   * @param txnum the id of the transaction performing the modification
   * @param lsn the LSN of the corresponding log record
   */
  public void setString(int offset, String val, int txnum, int lsn) {
    modifiedBy = txnum;
    if (lsn >= 0)
      logSequenceNumber = lsn;
    contents.setString(offset, val);
  }

  /**
   * Returns a reference to the disk block that the buffer is pinned to.<br>
   * 返回对固定缓冲区的磁盘块的引用。
   * 
   * @return a reference to a disk block
   */
  public Block block() {
    return blk;
  }

  /**
   * Writes the page to its disk block if the page is dirty. The method ensures
   * that the corresponding log record has been written to disk prior to writing
   * the page to disk.<br>
   * 如果页面脏，将页面写入其磁盘块。 该方法确保在将页面写入磁盘之前已将相应的日志记录写入磁盘。
   */
  void flush() {
    if (modifiedBy >= 0) {
      SimpleDB.logMgr().flush(logSequenceNumber);
      contents.write(blk);
      modifiedBy = -1;
    }
  }

  /**
   * Increases the buffer's pin count.增加缓冲区的ping数。
   */
  void pin() {
    pins++;
  }

  /**
   * Decreases the buffer's pin count.减少缓冲区的ping数。
   */
  void unpin() {
    pins--;
  }

  /**
   * Returns true if the buffer is currently pinned (that is, if it has a
   * nonzero pin count).<br>
   * 如果缓冲区当前被固定（即，如果它具有非零引用数)，则返回true。
   * 
   * @return true if the buffer is pinned
   */
  boolean isPinned() {
    return pins > 0;
  }

  /**
   * Returns true if the buffer is dirty due to a modification by the specified
   * transaction.<br>
   * 如果缓冲区由于指定事务的修改而变脏，则返回true。
   * 
   * @param txnum the id of the transaction
   * @return true if the transaction modified the buffer
   */
  boolean isModifiedBy(int txnum) {
    return txnum == modifiedBy;
  }

  /**
   * Reads the contents of the specified block into the buffer's page. If the
   * buffer was dirty, then the contents of the previous page are first written
   * to disk.<br>
   * 将指定块的内容读入缓冲区页面。 如果缓冲区有脏数据，那么先前页面的内容首先写入磁盘。
   * 
   * @param b a reference to the data block
   */
  void assignToBlock(Block b) {
    flush();
    blk = b;
    contents.read(blk);
    pins = 0;
  }

  /**
   * Initializes the buffer's page according to the specified formatter, and
   * appends the page to the specified file. If the buffer was dirty, then the
   * contents of the previous page are first written to disk.<br>
   * 根据指定的格式化程序初始化缓冲区页面，并将页面附加到指定的文件。 如果缓冲区脏，那么先前页面的内容首先写入磁盘。
   * 
   * @param filename the name of the file 文件的名称
   * @param fmtr a page formatter, used to initialize the page 一个页面格式化程序，用于初始化页面
   */
  void assignToNew(String filename, PageFormatter fmtr) {
    flush();
    fmtr.format(contents);
    blk = contents.append(filename);
    pins = 0;
  }
}